home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume16 / deliver / part02 < prev    next >
Encoding:
Internet Message Format  |  1988-11-14  |  28.5 KB

  1. Subject:  v16i082:  Mail delivery program, Part02/03
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4.  
  5. Submitted-by: Chip Salzenberg <ateng!chip@uunet.uu.net>
  6. Posting-number: Volume 16, Issue 82
  7. Archive-name: deliver/part02
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line, then unpack
  11. # it by saving it into a file and typing "sh file".  To overwrite existing
  12. # files, type "sh file -c".  You can also feed this as standard input via
  13. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  14. # will see the following message at the end:
  15. #        "End of shell archive."
  16. # Contents:  context.c copymsg.c debug.c dest.c dfile.c lock.c
  17. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  18. if test -f 'context.c' -a "${1}" != "-c" ; then 
  19.   echo shar: Will not clobber existing file \"'context.c'\"
  20. else
  21. echo shar: Extracting \"'context.c'\" \(2596 characters\)
  22. sed "s/^X//" >'context.c' <<'END_OF_FILE'
  23. X/* $Header: context.c,v 1.3 88/09/14 19:41:40 network Exp $
  24. X *
  25. X * User context manager.
  26. X * This module exists for efficiency reasons; I could just call getpwnam()
  27. X * every time I need context info.
  28. X *
  29. X * $Log:    context.c,v $
  30. X * Revision 1.3  88/09/14  19:41:40  network
  31. X * Portability to System V and BSD.
  32. X * General fixup.
  33. X * 
  34. X * Revision 1.2  88/08/30  16:12:28  network
  35. X * Use savestr() instead of strdup().
  36. X * 
  37. X * Revision 1.1  88/06/06  09:38:05  chip
  38. X * Initial revision
  39. X * 
  40. X */
  41. X
  42. X#include "deliver.h"
  43. X#include <pwd.h>
  44. X#include <grp.h>
  45. X
  46. Xextern  struct passwd   *getpwnam();
  47. Xextern  struct passwd   *getpwuid();
  48. Xextern  struct group    *getgrnam();
  49. Xextern  struct group    *getgrgid();
  50. X
  51. X/*
  52. X * Local functions.
  53. X */
  54. X
  55. Xstatic  CONTEXT *new_context();
  56. X
  57. X/*
  58. X * Local data.
  59. X */
  60. X
  61. Xstatic  CONTEXT *ctlist;    /* Chain of CONTEXT structures.        */
  62. X
  63. X/*----------------------------------------------------------------------
  64. X * Look up a context by user name.
  65. X */
  66. X
  67. XCONTEXT *
  68. Xname_context(name)
  69. Xchar    *name;
  70. X{
  71. X    struct passwd *pw;
  72. X    CONTEXT *ct;
  73. X
  74. X    for (ct = ctlist; ct; ct = ct->next)
  75. X    {
  76. X        if (strcmp(ct->name, name) == 0)
  77. X            return ct;
  78. X    }
  79. X
  80. X    if ((pw = getpwnam(name)) == NULL)
  81. X        return NULL;
  82. X
  83. X    return new_context(pw);
  84. X}
  85. X
  86. X/*----------------------------------------------------------------------
  87. X * Look up a context by user ID.
  88. X */
  89. X
  90. XCONTEXT *
  91. Xuid_context(uid)
  92. Xint     uid;
  93. X{
  94. X    struct passwd *pw;
  95. X    CONTEXT *ct;
  96. X
  97. X    for (ct = ctlist; ct; ct = ct->next)
  98. X    {
  99. X        if (ct->uid == uid)
  100. X            return ct;
  101. X    }
  102. X
  103. X    if ((pw = getpwuid(uid)) == NULL)
  104. X        return NULL;
  105. X
  106. X    return new_context(pw);
  107. X}
  108. X
  109. X/*----------------------------------------------------------------------
  110. X * Local function -- create a new context structure and return
  111. X * its address.
  112. X */
  113. X
  114. Xstatic CONTEXT *
  115. Xnew_context(pw)
  116. Xstruct passwd *pw;
  117. X{
  118. X    CONTEXT *ct;
  119. X
  120. X    ct = (CONTEXT *) zalloc(sizeof(CONTEXT));
  121. X    ct->uid = pw->pw_uid;
  122. X    ct->gid = pw->pw_gid;
  123. X    ct->name = copystr(pw->pw_name);
  124. X    ct->home = copystr(pw->pw_dir);
  125. X
  126. X    ct->next = ctlist;
  127. X    ctlist = ct;
  128. X
  129. X    return ct;
  130. X}
  131. X
  132. X/*----------------------------------------------------------------------
  133. X * Report whether is is possible or not to enter the given context.
  134. X */
  135. X
  136. Xint
  137. Xok_context(ct)
  138. XCONTEXT *ct;
  139. X{
  140. X    if (! ct)
  141. X        return FALSE;
  142. X
  143. X    if (eff_uid == 0
  144. X     || ((real_uid == ct->uid) && (real_gid == ct->gid)))
  145. X        return TRUE;
  146. X    else
  147. X        return FALSE;
  148. X}
  149. X
  150. X/*----------------------------------------------------------------------
  151. X * Look up a group ID by name.
  152. X */
  153. X
  154. X#ifdef MAILBOX_GROUP
  155. X
  156. Xint
  157. Xgroup_id(name)
  158. Xchar    *name;
  159. X{
  160. X    struct group *grp;
  161. X
  162. X    if ((grp = getgrnam(name)) == NULL)
  163. X        return -1;
  164. X
  165. X    return grp->gr_gid;
  166. X}
  167. X
  168. X#endif /* MAILBOX_GROUP */
  169. END_OF_FILE
  170. if test 2596 -ne `wc -c <'context.c'`; then
  171.     echo shar: \"'context.c'\" unpacked with wrong size!
  172. fi
  173. # end of 'context.c'
  174. fi
  175. if test -f 'copymsg.c' -a "${1}" != "-c" ; then 
  176.   echo shar: Will not clobber existing file \"'copymsg.c'\"
  177. else
  178. echo shar: Extracting \"'copymsg.c'\" \(5998 characters\)
  179. sed "s/^X//" >'copymsg.c' <<'END_OF_FILE'
  180. X/* $Header: copymsg.c,v 1.1 88/06/06 09:38:13 chip Exp $
  181. X *
  182. X * Take the message from standard input and write it to two temp files,
  183. X * one for the header (including the empty line) and one for the body.
  184. X *
  185. X * $Log:    copymsg.c,v $
  186. X * Revision 1.1  88/06/06  09:38:13  chip
  187. X * Initial revision
  188. X * 
  189. X */
  190. X
  191. X#include "deliver.h"
  192. X
  193. X/*
  194. X * Macros.
  195. X */
  196. X
  197. X/* Does a string start with "From "? */
  198. X
  199. X#define ISFROM(p) ((p)[0] == 'F' && (p)[1] == 'r' && (p)[2] == 'o' \
  200. X        && (p)[3] == 'm' && (p)[4] == ' ')
  201. X
  202. X/*
  203. X * Local functions.
  204. X */
  205. X
  206. Xstatic  char    *tempfile();
  207. Xstatic  int     tcreate();
  208. X
  209. X/*----------------------------------------------------------------------
  210. X * Copy the message on the standard input to two temp files:
  211. X * one for the header and one for the body.
  212. X */
  213. X
  214. Xint
  215. Xcopy_message()
  216. X{
  217. X    char    buf[BUFSIZ];
  218. X    FILE    *dfp[T_MAX];
  219. X    char    *p, *fsender, *fremote, *osender, *oremote;
  220. X    long    now;
  221. X    int     t, b, empty_line;
  222. X    int     ret = 0;
  223. X
  224. X    /*
  225. X     * Create temporary files to hold the header and message body.
  226. X     */
  227. X
  228. X    for (t = 0; t < T_MAX; ++t)
  229. X    {
  230. X        int     fd;
  231. X
  232. X        tfile[t] = tempfile();
  233. X        if ((tfd[t] = tcreate(tfile[t])) == -1)
  234. X            return -1;
  235. X
  236. X        if ((fd = dup(tfd[t])) == -1)
  237. X        {
  238. X            syserr("dup %s fd", ttype[t]);
  239. X            return -1;
  240. X        }
  241. X        (void) lseek(fd, 0L, 0);
  242. X        if ((dfp[t] = fdopen(fd, "r+")) == NULL)
  243. X        {
  244. X            error("can't fdopen %s fd", ttype[t]);
  245. X            return -1;
  246. X        }
  247. X    }
  248. X
  249. X    /* Debugging message for later examination of temp files. */
  250. X
  251. X    if (verbose)
  252. X    {
  253. X        message("header=%s, body=%s\n",
  254. X            tfile[T_HEADER], tfile[T_BODY]);
  255. X    }
  256. X
  257. X    /*
  258. X     * If there is a From_ line, find the sender name therein.
  259. X     */
  260. X
  261. X    fsender = fremote = NULL;
  262. X
  263. X    b = (fgets(buf, GETSIZE(buf), stdin) ? TRUE : FALSE);
  264. X
  265. X    if (b && ISFROM(buf) && (p = strchr(buf, '\n')) != NULL)
  266. X    {
  267. X        b = FALSE;      /* Don't output two From_ lines */
  268. X        *p = 0;
  269. X
  270. X        /* Find sender */
  271. X
  272. X        for (fsender = buf + 5; isspace(*fsender); ++fsender)
  273. X            ; /* until sender */
  274. X        for (p = fsender; *p && !isspace(*p); ++p)
  275. X            ; /* until end of sender */
  276. X        if (*p)
  277. X            *p++ = '\0';
  278. X
  279. X        /* Find 'remote from' phrase (if any) */
  280. X
  281. X        for (fremote = p;
  282. X             (fremote = strchr(fremote, 'r')) != NULL;
  283. X             ++fremote)
  284. X        {
  285. X            if (strncmp(fremote, "remote from", 11) == 0)
  286. X            {
  287. X                fremote += 11;
  288. X                while (isspace(*fremote))
  289. X                    ++fremote;
  290. X                break;
  291. X            }
  292. X        }
  293. X    }
  294. X
  295. X    /*
  296. X     * Write a From_ line to the header file.
  297. X     * If the user specified a sender name, use it;
  298. X     * else if we found a From_ line, use the sender found therein;
  299. X     * else use the user name of our real UID.
  300. X     */
  301. X
  302. X    if (sender && *sender)
  303. X    {
  304. X        osender = sender;
  305. X        oremote = NULL;
  306. X    }
  307. X    else if (fsender)
  308. X    {
  309. X        osender = fsender;
  310. X        oremote = fremote;
  311. X    }
  312. X    else
  313. X    {
  314. X        osender = real_ct->name;
  315. X        oremote = NULL;
  316. X    }
  317. X
  318. X    (void) fputs("From ", dfp[T_HEADER]);
  319. X    if (oremote)
  320. X    {
  321. X        (void) fputs(oremote, dfp[T_HEADER]);
  322. X        (void) fputc('!', dfp[T_HEADER]);
  323. X    }
  324. X    (void) fputs(osender, dfp[T_HEADER]);
  325. X    (void) fputc(' ', dfp[T_HEADER]);
  326. X    (void) time(&now);
  327. X    (void) fputs(ctime(&now), dfp[T_HEADER]);
  328. X
  329. X    /*
  330. X     * Copy the rest of the header (if any).
  331. X     */
  332. X
  333. X    for (; !feof(stdin); b = FALSE)
  334. X    {
  335. X        if (!b)
  336. X        {
  337. X            if (fgets(buf, GETSIZE(buf), stdin))
  338. X                b = TRUE;
  339. X            else
  340. X                break;
  341. X        }
  342. X
  343. X        /* Empty line means "end of header" */
  344. X
  345. X        if (buf[0] == '\n')
  346. X        {
  347. X            b = FALSE;    /* Don't put this line in the body. */
  348. X            break;
  349. X        }
  350. X
  351. X        /*
  352. X         * A line too long to fit in buf[] can't be a header line.
  353. X         * At least, that's my opinion... :-)
  354. X         */
  355. X
  356. X        if (!strchr(buf, '\n'))
  357. X            break;
  358. X
  359. X        /*
  360. X         * If line begins with whitespace, it's a continuation.
  361. X         * Else if line begins with From_ or '>', prepend '>'.
  362. X         * Else if line doesn't look like a header, this must
  363. X         * be the beginning of the body.
  364. X         */
  365. X
  366. X        if (isspace(buf[0]))
  367. X            ;               /* continuation */
  368. X        else if (ISFROM(buf) || (buf[0] == '>'))
  369. X            (void) fputc('>', dfp[T_HEADER]);
  370. X        else
  371. X        {
  372. X            /* look for the colon on a header label */
  373. X
  374. X            p = buf;
  375. X            while (isalpha(*p) || *p == '-')
  376. X                ++p;
  377. X            if ((p == buf) || (*p != ':'))
  378. X                break;  /* Not a header line! */
  379. X        }
  380. X
  381. X        /* Write the line to the header file. */
  382. X
  383. X        (void) fputs(buf, dfp[T_HEADER]);
  384. X    }
  385. X
  386. X    /*
  387. X     * End the header file with a blank line.
  388. X     * This enables us to simply concatenate it with the body file
  389. X     * to produce a valid message.
  390. X     */
  391. X
  392. X    (void) fputc('\n', dfp[T_HEADER]);
  393. X
  394. X    /*
  395. X     * Copy the body (if any).
  396. X     */
  397. X
  398. X    empty_line = FALSE;
  399. X    for (; !feof(stdin); b = FALSE)
  400. X    {
  401. X        if (!b)
  402. X        {
  403. X            if (fgets(buf, GETSIZE(buf), stdin))
  404. X                b = TRUE;
  405. X            else
  406. X                break;
  407. X        }
  408. X
  409. X        if (ISFROM(buf))
  410. X            (void) fputc('>', dfp[T_BODY]);
  411. X        (void) fputs(buf, dfp[T_BODY]);
  412. X
  413. X        empty_line = (buf[0] == '\n');
  414. X
  415. X        /*
  416. X         * Output the rest of a very long line.
  417. X         * We do this here, instead of going around the loop,
  418. X         * in order to avoid misinterpreting From_ strings
  419. X         * that may be found in long lines.
  420. X         */
  421. X
  422. X        while (!strchr(buf, '\n')
  423. X            && !feof(stdin)
  424. X            && fgets(buf, GETSIZE(buf), stdin))
  425. X            (void) fputs(buf, dfp[T_BODY]);
  426. X    }
  427. X
  428. X    /* Ensure that the body ends with a blank line. */
  429. X
  430. X    if (! empty_line)
  431. X        (void) fputc('\n', dfp[T_BODY]);
  432. X
  433. X    /*
  434. X     * If we encountered any trouble writing to the temp files,
  435. X     * let's not keep it secret.
  436. X     */
  437. X
  438. X    for (t = 0; t < T_MAX; ++t)
  439. X    {
  440. X        if (ferror(dfp[t]))
  441. X        {
  442. X            error("error writing to %s file %s\n",
  443. X                ttype[t], tfile[t]);
  444. X            ret = -1;
  445. X        }
  446. X
  447. X        (void) fclose(dfp[t]);
  448. X    }
  449. X
  450. X    /* Return error/success. */
  451. X
  452. X    return ret;
  453. X}
  454. X
  455. X/*----------------------------------------------------------------------
  456. X * Return a pointer to a temporary filename, or NULL if error.
  457. X */
  458. X
  459. Xstatic char *
  460. Xtempfile()
  461. X{
  462. X    static char template[] = "/tmp/dl.XXXXXX";
  463. X    char    *f;
  464. X
  465. X    f = zalloc(32);
  466. X    (void) strcpy(f, template);
  467. X    if (mktemp(f) == NULL)
  468. X    {
  469. X        error("can't create temporary file");
  470. X        return NULL;
  471. X    }
  472. X    return f;
  473. X}
  474. X
  475. X/*----------------------------------------------------------------------
  476. X * Create a file, or complain if it doesn't work.
  477. X */
  478. X
  479. Xstatic int
  480. Xtcreate(name)
  481. Xchar    *name;
  482. X{
  483. X    int     fd;
  484. X
  485. X    if ((fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
  486. X    {
  487. X        syserr("can't create %s", name);
  488. X        return -1;
  489. X    }
  490. X
  491. X    return fd;
  492. X}
  493. X
  494. END_OF_FILE
  495. if test 5998 -ne `wc -c <'copymsg.c'`; then
  496.     echo shar: \"'copymsg.c'\" unpacked with wrong size!
  497. fi
  498. # end of 'copymsg.c'
  499. fi
  500. if test -f 'debug.c' -a "${1}" != "-c" ; then 
  501.   echo shar: Will not clobber existing file \"'debug.c'\"
  502. else
  503. echo shar: Extracting \"'debug.c'\" \(1003 characters\)
  504. sed "s/^X//" >'debug.c' <<'END_OF_FILE'
  505. X/* $Header: debug.c,v 1.1 88/06/06 09:38:23 chip Exp $
  506. X *
  507. X * Debugging output.
  508. X *
  509. X * $Log:    debug.c,v $
  510. X * Revision 1.1  88/06/06  09:38:23  chip
  511. X * Initial revision
  512. X * 
  513. X */
  514. X
  515. X#include "deliver.h"
  516. X
  517. X/*----------------------------------------------------------------------
  518. X * Print out a complete dump of all destinations
  519. X */
  520. X
  521. Xdumpdests(when)
  522. Xchar    *when;
  523. X{
  524. X    DEST    *d;
  525. X
  526. X    message("Destinations %s:\n", when);
  527. X    for (d = first_dest(); d; d = next_dest(d))
  528. X    {
  529. X        message("\t%s", d->name);
  530. X        if (d->class == CL_MBOX)
  531. X            message(":%s", d->mailbox);
  532. X
  533. X        message(" (");
  534. X        switch (d->class)
  535. X        {
  536. X        case CL_USER:
  537. X            message("User");
  538. X            break;
  539. X        case CL_MBOX:
  540. X            message("Mailbox");
  541. X            break;
  542. X        case CL_UUCP:
  543. X            message("UUCP");
  544. X            break;
  545. X        }
  546. X        message(", ");
  547. X        switch (d->state)
  548. X        {
  549. X        case ST_WORKING:
  550. X            message("Working");
  551. X            break;
  552. X        case ST_HOLD:
  553. X            message("Hold");
  554. X            break;
  555. X        case ST_DONE:
  556. X            message("Done");
  557. X            break;
  558. X        case ST_ERROR:
  559. X            message("Error: %s", d->error);
  560. X            break;
  561. X        }
  562. X        message(")\n");
  563. X    }
  564. X}
  565. END_OF_FILE
  566. if test 1003 -ne `wc -c <'debug.c'`; then
  567.     echo shar: \"'debug.c'\" unpacked with wrong size!
  568. fi
  569. # end of 'debug.c'
  570. fi
  571. if test -f 'dest.c' -a "${1}" != "-c" ; then 
  572.   echo shar: Will not clobber existing file \"'dest.c'\"
  573. else
  574. echo shar: Extracting \"'dest.c'\" \(2079 characters\)
  575. sed "s/^X//" >'dest.c' <<'END_OF_FILE'
  576. X/* $Header: dest.c,v 1.2 88/08/30 16:12:40 network Exp $
  577. X *
  578. X * Operations on the list of mail destinations.
  579. X *
  580. X * $Log:    dest.c,v $
  581. X * Revision 1.2  88/08/30  16:12:40  network
  582. X * Use savestr() instead of strdup().
  583. X * 
  584. X * Revision 1.1  88/06/06  09:38:29  chip
  585. X * Initial revision
  586. X * 
  587. X */
  588. X
  589. X#include "deliver.h"
  590. X
  591. X/*
  592. X * Local data.
  593. X */
  594. X
  595. Xstatic  DEST    deadhead = { &deadhead, &deadhead };
  596. X#define HEADPTR    (&deadhead)
  597. X
  598. X/*----------------------------------------------------------------------
  599. X * Add a new destination to the list (unless it already exists).
  600. X * Return pointer to DEST.
  601. X */
  602. X
  603. XDEST *
  604. Xdest(name, mailbox)
  605. Xchar    *name;
  606. Xchar    *mailbox;
  607. X{
  608. X    DEST    *d;
  609. X    DCLASS   class;
  610. X
  611. X    if (strchr(name, '!'))
  612. X        class = CL_UUCP;
  613. X    else if (mailbox)
  614. X        class = CL_MBOX;
  615. X    else
  616. X        class = CL_USER;
  617. X
  618. X    for (d = HEADPTR->next; d != HEADPTR; d = d->next)
  619. X    {
  620. X        if (d->class != class)
  621. X            continue;
  622. X
  623. X        if (strcmp(d->name, name) != 0)
  624. X            continue;
  625. X
  626. X        /*
  627. X         * If this destination has a named mailbox, then
  628. X         * test it for equality as well.
  629. X         */
  630. X
  631. X        if (class == CL_MBOX
  632. X         && strcmp(d->mailbox, mailbox) != 0)
  633. X            continue;
  634. X
  635. X        /*
  636. X         * Like, gnarly, dude!  It's already in the chain!
  637. X         */
  638. X
  639. X        return d;
  640. X    }
  641. X
  642. X    /*
  643. X     * The given dest isn't in the list, so we have to add it.
  644. X     */
  645. X
  646. X    d = (DEST *) zalloc(sizeof(DEST));
  647. X    d->class = class;
  648. X    d->state = ST_WORKING;
  649. X    d->name = copystr(name);
  650. X    if (class == CL_MBOX)
  651. X        d->mailbox = copystr(mailbox);
  652. X
  653. X    if (class != CL_UUCP
  654. X     && name_context(name) == NULL)
  655. X    {
  656. X        d->state = ST_ERROR;
  657. X        d->error = "No such user";
  658. X    }
  659. X
  660. X    d->prev = HEADPTR->prev;
  661. X    d->next = HEADPTR;
  662. X    d->prev->next = d;
  663. X    d->next->prev = d;
  664. X
  665. X    return d;
  666. X}
  667. X
  668. X/*----------------------------------------------------------------------
  669. X * Return pointer to first DEST in the list.
  670. X */
  671. X
  672. XDEST *
  673. Xfirst_dest()
  674. X{
  675. X    if (HEADPTR->next != HEADPTR)
  676. X        return HEADPTR->next;
  677. X
  678. X    return NULL;
  679. X}
  680. X
  681. X/*----------------------------------------------------------------------
  682. X * Return pointer to next DEST in the list, or NULL.
  683. X */
  684. X
  685. XDEST *
  686. Xnext_dest(d)
  687. XDEST    *d;
  688. X{
  689. X    if (d && (d = d->next) != HEADPTR)
  690. X        return d;
  691. X
  692. X    return NULL;
  693. X}
  694. END_OF_FILE
  695. if test 2079 -ne `wc -c <'dest.c'`; then
  696.     echo shar: \"'dest.c'\" unpacked with wrong size!
  697. fi
  698. # end of 'dest.c'
  699. fi
  700. if test -f 'dfile.c' -a "${1}" != "-c" ; then 
  701.   echo shar: Will not clobber existing file \"'dfile.c'\"
  702. else
  703. echo shar: Extracting \"'dfile.c'\" \(6731 characters\)
  704. sed "s/^X//" >'dfile.c' <<'END_OF_FILE'
  705. X/* $Header: dfile.c,v 1.2 88/08/25 15:23:56 network Exp $
  706. X *
  707. X * Filter destination(s) through delivery file(s).
  708. X *
  709. X * $Log:    dfile.c,v $
  710. X * Revision 1.2  88/08/25  15:23:56  network
  711. X * Add third parameter to do_dfile(), so that if the delivery file cannot
  712. X * be executed, the given destination can be recorded as an error.
  713. X * 
  714. X * Revision 1.1  88/06/06  09:38:38  chip
  715. X * Initial revision
  716. X * 
  717. X */
  718. X
  719. X#include "deliver.h"
  720. X#include <sys/types.h>
  721. X#include <sys/stat.h>
  722. X
  723. X/*----------------------------------------------------------------------
  724. X * Filter all valid destinations through the global delivery file.
  725. X */
  726. X
  727. Xsys_dfile(dac, dav)
  728. Xint     dac;
  729. Xchar    **dav;
  730. X{
  731. X    char    **fav;
  732. X    int     fac, a, goodnames;
  733. X    struct stat st;
  734. X
  735. X    /*
  736. X     * If there is no global delivery file, then take all the named
  737. X     * addresses verbatim and return.
  738. X     */
  739. X
  740. X    if (stat(sys_deliver, &st) == -1)
  741. X    {
  742. X        if (verbose)
  743. X            message("%s: no system delivery file\n",
  744. X                progname);
  745. X
  746. X        for (a = 0; a < dac; ++a)
  747. X            (void) dest(dav[a], (char *) NULL);
  748. X        return;
  749. X    }
  750. X
  751. X    /*
  752. X     * Collect the arguments for the delivery file.
  753. X     */
  754. X
  755. X    fav = (char **) zalloc((dac + 3) * sizeof(char **));
  756. X    fav[0] = shell;
  757. X    fav[1] = sys_deliver;
  758. X    fac = 2;
  759. X
  760. X    goodnames = 0;
  761. X    for (a = 0; a < dac; ++a)
  762. X    {
  763. X        char    *p;
  764. X
  765. X        for (p = dav[a]; *p; ++p)
  766. X        {
  767. X            if (!isalpha(*p)
  768. X             && !isdigit(*p)
  769. X             && !strchr("#%-+._", *p))
  770. X                break;
  771. X        }
  772. X
  773. X        if (*p)
  774. X        {
  775. X            /* Invalid name -- note it and go on. */
  776. X
  777. X            (void) dest(dav[a], (char *) NULL);
  778. X        }
  779. X        else
  780. X        {
  781. X            /* Valid name -- let the delivery file handle it. */
  782. X
  783. X            fav[fac++] = dav[a];
  784. X            ++goodnames;
  785. X        }
  786. X    }
  787. X
  788. X    fav[fac] = NULL;
  789. X
  790. X    /*
  791. X     * If there were any good names found, let loose the delivery
  792. X     * file.  Note the meaning of "good" is "well-formed", not "valid".
  793. X     * Thus the system delivery file has control over the handling of
  794. X     * all local deliveries, not just those to valid users.
  795. X     */
  796. X
  797. X    if (goodnames)
  798. X        (void) do_dfile(eff_ct, fav, (DEST *)NULL);
  799. X
  800. X    free((char *) fav);
  801. X}
  802. X
  803. X/*----------------------------------------------------------------------
  804. X * Filter all user destinations through their local delivery files.
  805. X */
  806. X
  807. Xuser_dfiles()
  808. X{
  809. X    DEST    *d;
  810. X    int     nfound;
  811. X
  812. X    /*
  813. X     * Continue to loop through all addresses until no destination
  814. X     * that needs expanding can be found.
  815. X     */
  816. X
  817. X    do {
  818. X        nfound = 0;
  819. X        for (d = first_dest(); d; d = next_dest(d))
  820. X        {
  821. X            if (d->class == CL_USER
  822. X             && d->state == ST_WORKING
  823. X             && !d->dfdone)
  824. X            {
  825. X                one_dfile(d);
  826. X                d->dfdone = TRUE;
  827. X            }
  828. X        }
  829. X    } while (nfound > 0);
  830. X}
  831. X
  832. X/*----------------------------------------------------------------------
  833. X * Run the delivery file (if any) for the specified destination.
  834. X */
  835. X
  836. Xone_dfile(d)
  837. XDEST    *d;
  838. X{
  839. X    CONTEXT *ct;
  840. X    char    *fav[4];
  841. X    char    udel_path[100];
  842. X    struct stat st;
  843. X
  844. X    if ((ct = name_context(d->name)) == NULL)
  845. X    {
  846. X        d->state = ST_ERROR;
  847. X        d->error = "Missing context in user_dfile_one()";
  848. X        return;
  849. X    }
  850. X
  851. X    /*
  852. X     * If user's home directory is missing, forget it.
  853. X     * If user's home directory is writable to the world,
  854. X     * executing the delivery file would allow a security breach!
  855. X     * Thanks to Jon Zeeff for this hint...
  856. X     */
  857. X
  858. X    if (stat(ct->home, &st) == -1
  859. X     || (st.st_mode & S_IFMT) != S_IFDIR)
  860. X    {
  861. X        if (verbose)
  862. X            message("%s: home directory %s is missing!\n",
  863. X                ct->name, ct->home);
  864. X        return;
  865. X    }
  866. X
  867. X    if (st.st_mode & 02)
  868. X    {
  869. X        if (verbose)
  870. X            message("%s: home directory is writable to the world!\n",
  871. X                ct->name);
  872. X        return;
  873. X    }
  874. X
  875. X    /*
  876. X     * If there is no delivery file to execute, just return.
  877. X     */
  878. X
  879. X    (void) sprintf(udel_path, "%s/%s", ct->home, user_deliver);
  880. X    if (stat(udel_path, &st) == -1)
  881. X    {
  882. X        if (verbose)
  883. X            message("%s has no delivery file\n", d->name);
  884. X        return;
  885. X    }
  886. X
  887. X    /*
  888. X     * Time to run the file!
  889. X     * We put this dest on hold, so that it will be ignored unless
  890. X     * the delivery file names it.
  891. X     */
  892. X
  893. X    d->state = ST_HOLD;
  894. X
  895. X    fav[0] = "sh";
  896. X    fav[1] = udel_path;
  897. X    fav[2] = d->name;
  898. X    fav[3] = NULL;
  899. X    (void) do_dfile(ct, fav, d);
  900. X}
  901. X
  902. X/*----------------------------------------------------------------------
  903. X * Process a delivery file.
  904. X */
  905. X
  906. Xint
  907. Xdo_dfile(ct, av, d)
  908. XCONTEXT *ct;
  909. Xchar    **av;
  910. XDEST    *d;
  911. X{
  912. X    FILE    *fp;
  913. X    char    *name, *mailbox;
  914. X
  915. X    if (!ct)
  916. X        return -1;
  917. X
  918. X    if (! ok_context(ct))
  919. X    {
  920. X        if (d)
  921. X        {
  922. X            d->state = ST_ERROR;
  923. X            d->error = "No permissions for that context";
  924. X        }
  925. X        else
  926. X            message("No permissions to run as %s\n", ct);
  927. X
  928. X        return -1;
  929. X    }
  930. X
  931. X    /* Make sure that the temp files are readable to the new process. */
  932. X
  933. X    give_temps(ct);
  934. X
  935. X    /* Here we go! */
  936. X
  937. X    if (verbose)
  938. X        message("Processing delivery file as %s\n", ct->name);
  939. X
  940. X    if ((fp = ct_popenv(ct, "/bin/sh", av, "r")) == NULL)
  941. X    {
  942. X        error("can't execute delivery file as %s\n", ct->name);
  943. X        leave(1);
  944. X    }
  945. X
  946. X    /*
  947. X     * Read the standard output of the delivery file.
  948. X     */
  949. X
  950. X    while (dfile_gets(fp, &name, &mailbox) >= 0)
  951. X    {
  952. X        DEST    *nd;
  953. X
  954. X        nd = dest(name, mailbox);
  955. X        if (nd->state == ST_HOLD)
  956. X            nd->state = ST_WORKING;
  957. X
  958. X        /*
  959. X         * If the delivery file specified a mailbox, verify
  960. X         * that the user whose delivery file is running has
  961. X         * permissions for the requested context.
  962. X         */
  963. X
  964. X        if ((nd->state == ST_WORKING) && (mailbox != NULL))
  965. X        {
  966. X            CONTEXT *nct;
  967. X
  968. X            if ((nct = name_context(name)) == NULL)
  969. X            {
  970. X                nd->state = ST_ERROR;
  971. X                nd->error = "Lost context in do_dfile()";
  972. X            }
  973. X            else if (! ok_context(nct))
  974. X            {
  975. X                nd->state = ST_ERROR;
  976. X                nd->error = "No permissions for that context";
  977. X            }
  978. X        }
  979. X    }
  980. X
  981. X    return ct_pclose(fp);
  982. X}
  983. X
  984. X/*----------------------------------------------------------------------
  985. X * Get and parse a single delivery file output line.
  986. X */
  987. X
  988. Xint
  989. Xdfile_gets(fp, namep, mailboxp)
  990. XFILE    *fp;
  991. Xchar    **namep;
  992. Xchar    **mailboxp;
  993. X{
  994. X    char    *p, *q;
  995. X    static  char    buf[BUFSIZ];
  996. X
  997. X    if (fgets(buf, GETSIZE(buf), fp) == NULL)
  998. X        return -1;
  999. X
  1000. X    if ((p = strchr(buf, '\n')) != NULL)
  1001. X        *p = 0;
  1002. X    else
  1003. X    {
  1004. X        int c;
  1005. X
  1006. X        while ((c = fgetc(fp)) != '\n' && c != EOF)
  1007. X            ; /* keep reading */
  1008. X
  1009. X        error("invalid line from delivery file: '%s'\n", buf);
  1010. X        return -1;
  1011. X    }
  1012. X
  1013. X    /* Strip out all whitespace and eliminate duplicated slashes */
  1014. X
  1015. X    p = q = buf;
  1016. X    while (*p)
  1017. X    {
  1018. X        if (! isspace(*p))
  1019. X        {
  1020. X            if ((*q++ = *p++) == '/')
  1021. X            {
  1022. X                while (*p == '/')
  1023. X                    ++p;
  1024. X            }
  1025. X        }
  1026. X    }
  1027. X    *q = 0;
  1028. X
  1029. X    /* Debugging message: display input line */
  1030. X
  1031. X    if (verbose)
  1032. X        message("\t'%s'\n", buf);
  1033. X
  1034. X    if ((p = strchr(buf, ':')) != NULL)
  1035. X    {
  1036. X        *p++ = 0;
  1037. X        if ((q = strchr(p, ':')) != NULL)
  1038. X            *q = 0;
  1039. X    }
  1040. X
  1041. X    *namep = buf;
  1042. X    *mailboxp = p;
  1043. X    return 0;
  1044. X}
  1045. X
  1046. X/*----------------------------------------------------------------------
  1047. X * Make the temp files readable in the given context.
  1048. X * This is needed because the temps are readable by owner only.
  1049. X */
  1050. X
  1051. Xgive_temps(ct)
  1052. XCONTEXT *ct;
  1053. X{
  1054. X    int     t;
  1055. X
  1056. X    if (!ct)
  1057. X        return;
  1058. X
  1059. X    for (t = 0; t < T_MAX; ++t)
  1060. X    {
  1061. X        if (chown(tfile[t], ct->uid, ct->gid) == -1)
  1062. X            syserr("can't chown %s", tfile[t]);
  1063. X    }
  1064. X}
  1065. END_OF_FILE
  1066. if test 6731 -ne `wc -c <'dfile.c'`; then
  1067.     echo shar: \"'dfile.c'\" unpacked with wrong size!
  1068. fi
  1069. # end of 'dfile.c'
  1070. fi
  1071. if test -f 'lock.c' -a "${1}" != "-c" ; then 
  1072.   echo shar: Will not clobber existing file \"'lock.c'\"
  1073. else
  1074. echo shar: Extracting \"'lock.c'\" \(6559 characters\)
  1075. sed "s/^X//" >'lock.c' <<'END_OF_FILE'
  1076. X/* $Header: lock.c,v 1.2 88/08/30 16:13:14 network Exp $
  1077. X *
  1078. X * Mailbox locking.
  1079. X * Local hacks for mailbox access should be grafted here.
  1080. X *
  1081. X * $Log:    lock.c,v $
  1082. X * Revision 1.2  88/08/30  16:13:14  network
  1083. X * Portability fixes from Ronald Karr <tron@uts.amdahl.com>.
  1084. X * 
  1085. X * Revision 1.1  88/06/06  09:38:48  chip
  1086. X * Initial revision
  1087. X * 
  1088. X */
  1089. X
  1090. X#include "deliver.h"
  1091. X
  1092. X/*
  1093. X * Validate the locking configuration.
  1094. X */
  1095. X
  1096. X#if (defined(ML_FCNTL) + defined(ML_LOCKF) + defined(ML_LOCKING)) > 1
  1097. X  lose! "Only one of ML_FCNTL, ML_LOCKF and ML_LOCKING may be defined.";
  1098. X#endif
  1099. X
  1100. X/*
  1101. X * Support for the lockf() system call.
  1102. X */
  1103. X
  1104. X#ifdef ML_LOCKF
  1105. X#include <unistd.h>
  1106. X#define SIMPLE_LOCK "lockf"
  1107. X#define LOCKFD(fd, size)    lockf(fd, F_LOCK, size)
  1108. X#define UNLOCKFD(fd, size)  lockf(fd, F_ULOCK, size)
  1109. X#endif /* ML_LOCKF */
  1110. X
  1111. X/*
  1112. X * Setup for the locking() system call.
  1113. X */
  1114. X
  1115. X#ifdef ML_LOCKING
  1116. X#include <sys/types.h>
  1117. X#include <sys/locking.h>
  1118. X#define SIMPLE_LOCK "locking"
  1119. X#define LOCKFD(fd, size)    locking(fd, LK_LOCK, size)
  1120. X#define UNLOCKFD(fd, size)  locking(fd, LK_UNLOCK, size)
  1121. X#endif
  1122. X
  1123. X/*
  1124. X * Local functions.
  1125. X */
  1126. X
  1127. X#ifdef ML_DOTLOCK
  1128. Xstatic  char    *dotlock_name();
  1129. X#endif
  1130. X#ifdef ML_DOTMLK
  1131. Xstatic  char    *dotmlk_name();
  1132. X#endif
  1133. X
  1134. X/*----------------------------------------------------------------------
  1135. X * Lock a mailbox by name.
  1136. X *
  1137. X * This code looks quite hairy with all the ifdefs.  In fact, the only
  1138. X * somewhat strange thing here is that neither, either, or both of
  1139. X * ML_DOTLOCK and ML_DOTMLK may be defined, and we have to allow for it.
  1140. X */
  1141. X
  1142. Xint
  1143. Xlock_name(name)
  1144. Xchar    *name;
  1145. X{
  1146. X#ifdef ML_DOTLOCK
  1147. X    char    *dotlock;
  1148. X#endif
  1149. X#ifdef ML_DOTMLK
  1150. X    char    *dotmlk;
  1151. X#endif
  1152. X
  1153. X#ifdef ML_DOTLOCK
  1154. X    if ((dotlock = dotlock_name(name)) == NULL
  1155. X     || create_lockfile(dotlock) < 0)
  1156. X        return -1;
  1157. X#endif /* ML_DOTLOCK */
  1158. X
  1159. X#ifdef ML_DOTMLK
  1160. X    if ((dotmlk = dotmlk_name(name)) == NULL
  1161. X     || create_lockfile(dotmlk) < 0)
  1162. X    {
  1163. X#ifdef ML_DOTLOCK
  1164. X        (void) remove_lockfile(dotlock); /* don't leave me hanging */
  1165. X#endif
  1166. X        return -1;
  1167. X    }
  1168. X#endif /* ML_DOTMLK */
  1169. X
  1170. X    return 0;
  1171. X}
  1172. X
  1173. X/*----------------------------------------------------------------------
  1174. X * Unlock a mailbox by name.
  1175. X */
  1176. X
  1177. Xint
  1178. Xunlock_name(name)
  1179. Xchar    *name;
  1180. X{
  1181. X    int     ret = 0;
  1182. X
  1183. X#ifdef ML_DOTLOCK
  1184. X    char    *dotlock;
  1185. X#endif
  1186. X#ifdef ML_DOTMLK
  1187. X    char    *dotmlk;
  1188. X#endif
  1189. X
  1190. X#ifdef ML_DOTLOCK
  1191. X    if ((dotlock = dotlock_name(name)) == NULL
  1192. X     || remove_lockfile(dotlock) < 0)
  1193. X        ret = -1;
  1194. X#endif /* ML_DOTLOCK */
  1195. X
  1196. X#ifdef ML_DOTMLK
  1197. X    if ((dotmlk = dotmlk_name(name)) == NULL
  1198. X     || remove_lockfile(dotmlk) < 0)
  1199. X        ret = -1;
  1200. X#endif /* ML_DOTMLK */
  1201. X
  1202. X    return ret;
  1203. X}
  1204. X
  1205. X/*----------------------------------------------------------------------
  1206. X * Lock a file descriptor.
  1207. X */
  1208. X
  1209. Xint
  1210. Xlock_fd(fd)
  1211. Xint     fd;
  1212. X{
  1213. X#ifdef ML_FCNTL
  1214. X    struct flock fl;
  1215. X
  1216. X    fl.l_type = F_WRLCK;
  1217. X    fl.l_whence = 0;
  1218. X    fl.l_start = 0L;
  1219. X    fl.l_len = 0L;
  1220. X
  1221. X    if (fcntl(fd, F_SETLKW, &fl) == -1)
  1222. X    {
  1223. X        syserr("can't lock with fcntl()");
  1224. X        return -1;
  1225. X    }
  1226. X
  1227. X    if (verbose)
  1228. X        message("locked mailbox with fcntl()\n");
  1229. X#endif /* ML_FCNTL */
  1230. X
  1231. X#ifdef SIMPLE_LOCK
  1232. X    long    pos;
  1233. X
  1234. X    if ((pos = lseek(fd, 0L, 0)) == -1)
  1235. X    {
  1236. X        syserr("can't seek in mailbox");
  1237. X        return -1;
  1238. X    }
  1239. X    if (LOCKFD(fd, 0L) == -1)
  1240. X    {
  1241. X        syserr("can't lock with %s()", SIMPLE_LOCK);
  1242. X        return -1;
  1243. X    }
  1244. X    if (lseek(fd, pos, 0) == -1)
  1245. X    {
  1246. X        syserr("can't seek in mailbox");
  1247. X        return -1;
  1248. X    }
  1249. X
  1250. X    if (verbose)
  1251. X        message("locked mailbox with %s()\n", SIMPLE_LOCK);
  1252. X#endif /* SIMPLE_LOCK */
  1253. X
  1254. X    /* Default: success */
  1255. X    return 0;
  1256. X}
  1257. X
  1258. X/*----------------------------------------------------------------------
  1259. X * Unlock a file descriptor.
  1260. X */
  1261. X
  1262. Xint
  1263. Xunlock_fd(fd)
  1264. Xint     fd;
  1265. X{
  1266. X#ifdef ML_FCNTL
  1267. X    struct flock fl;
  1268. X
  1269. X    fl.l_type = F_UNLCK;
  1270. X    fl.l_whence = 0;
  1271. X    fl.l_start = 0L;
  1272. X    fl.l_len = 0L;
  1273. X
  1274. X    if (fcntl(fd, F_SETLKW, &fl) == -1)
  1275. X    {
  1276. X        syserr("can't unlock with fcntl()");
  1277. X        return -1;
  1278. X    }
  1279. X
  1280. X    if (verbose)
  1281. X        message("unlocked mailbox with fcntl()\n");
  1282. X#endif /* ML_FCNTL */
  1283. X
  1284. X#ifdef SIMPLE_LOCK
  1285. X    long    pos;
  1286. X
  1287. X    if ((pos = lseek(fd, 0L, 0)) == -1)
  1288. X    {
  1289. X        syserr("can't seek in mailbox");
  1290. X        return -1;
  1291. X    }
  1292. X    if (LOCKFD(fd, 0L) == -1)
  1293. X    {
  1294. X        syserr("can't unlock with %s()", SIMPLE_LOCK);
  1295. X        return -1;
  1296. X    }
  1297. X    if (lseek(fd, pos, 0) == -1)
  1298. X    {
  1299. X        syserr("can't seek in mailbox");
  1300. X        return -1;
  1301. X    }
  1302. X
  1303. X    if (verbose)
  1304. X        message("unlocked mailbox with %s()\n", SIMPLE_LOCK);
  1305. X#endif /* SIMPLE_LOCK */
  1306. X
  1307. X    /* Default: success */
  1308. X    return 0;
  1309. X}
  1310. X
  1311. X/*----------------------------------------------------------------------
  1312. X * Return the name of the appropriate ".lock" file for a mailbox.
  1313. X */
  1314. X
  1315. X#ifdef ML_DOTLOCK
  1316. X
  1317. Xstatic char *
  1318. Xdotlock_name(name)
  1319. Xchar    *name;
  1320. X{
  1321. X    static char *lname = NULL;
  1322. X    static int lsize = 0;
  1323. X    char    *p;
  1324. X    int     n, i;
  1325. X
  1326. X    n = strlen(name);
  1327. X    if (lsize < n + 8)
  1328. X    {
  1329. X        if (lname)
  1330. X            free(lname);
  1331. X        lsize = n + 32;
  1332. X        lname = zalloc(lsize);
  1333. X    }
  1334. X
  1335. X    (void) strcpy(lname, name);
  1336. X
  1337. X    /*
  1338. X     * We want as much of `basename.lock' as will fit in a string
  1339. X     * MAX_NAMESIZE long.
  1340. X     */
  1341. X    for (i = 0, p = basename(lname); (i < MAX_NAMESIZE - 5) && (*p); ++i)
  1342. X        ++p;
  1343. X    (void) strcpy(p, ".lock");
  1344. X
  1345. X    return lname;
  1346. X}
  1347. X
  1348. X#endif /* ML_DOTLOCK */
  1349. X
  1350. X/*----------------------------------------------------------------------
  1351. X * Return the name of the appropriate ".mlk" file for a mailbox.
  1352. X */
  1353. X
  1354. X#ifdef ML_DOTMLK
  1355. X
  1356. Xstatic char *
  1357. Xdotmlk_name(name)
  1358. Xchar    *name;
  1359. X{
  1360. X    static char lname[MAX_NAMESIZE + 16];
  1361. X    char    *p, *d;
  1362. X    int     i;
  1363. X
  1364. X    /*
  1365. X     * To explain the below:  If we ass_u_me that MAX_NAMESIZE is 14,
  1366. X     * then this code is like `printf(lname, "/tmp/%.10s.mlk", ...)'.
  1367. X     * In other words, we want as much of `basename.mlk' as will fit
  1368. X     * in a string MAX_NAMESIZE long.
  1369. X     */
  1370. X    d = lname;
  1371. X    for (p = "/tmp/"; *p; )
  1372. X        *d++ = *p++;
  1373. X    for (i = 0, p = basename(name); (i < MAX_NAMESIZE - 4) && (*p); ++i)
  1374. X        *d++ = *p++;
  1375. X    (void) strcpy(d, ".mlk");
  1376. X
  1377. X    return lname;
  1378. X}
  1379. X
  1380. X#endif /* ML_DOTMLK */
  1381. X
  1382. X/*----------------------------------------------------------------------
  1383. X * Create a lockfile.
  1384. X */
  1385. X
  1386. Xint
  1387. Xcreate_lockfile(name)
  1388. Xchar    *name;
  1389. X{
  1390. X    int     tries, fd;
  1391. X
  1392. X    for (tries = 0; tries < 10; ++tries)
  1393. X    {
  1394. X        if (tries)
  1395. X            snooze(3);
  1396. X
  1397. X        if ((fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0)) >= 0)
  1398. X        {
  1399. X            (void) close(fd);
  1400. X            if (verbose)
  1401. X                message("created lockfile %s\n", name);
  1402. X            return 0;
  1403. X        }
  1404. X
  1405. X        if (verbose && (tries == 0))
  1406. X        {
  1407. X            message("Waiting to create %s (try #%d)\n",
  1408. X                name, tries + 1);
  1409. X        }
  1410. X    }
  1411. X
  1412. X    syserr("can't create lockfile %s", name);
  1413. X    return -1;
  1414. X}
  1415. X
  1416. X/*----------------------------------------------------------------------
  1417. X * Remove a lockfile.
  1418. X */
  1419. X
  1420. Xint
  1421. Xremove_lockfile(name)
  1422. Xchar    *name;
  1423. X{
  1424. X    if (unlink(name) == -1)
  1425. X    {
  1426. X        syserr("can't remove lockfile %s", name);
  1427. X        return -1;
  1428. X    }
  1429. X
  1430. X    if (verbose)
  1431. X        message("removed lockfile %s\n", name);
  1432. X
  1433. X    return 0;
  1434. X}
  1435. END_OF_FILE
  1436. if test 6559 -ne `wc -c <'lock.c'`; then
  1437.     echo shar: \"'lock.c'\" unpacked with wrong size!
  1438. fi
  1439. # end of 'lock.c'
  1440. fi
  1441. echo shar: End of shell archive.
  1442. exit 0
  1443.  
  1444.